#include "MTContainerVariableDefinition.hpp"
#include "Params.h"
#include <sstream>
#include <iostream>

namespace MathematicaTranslator
{
	std::string* MTContainerVariableDefinition::generateCode(CodeGenContext& context)
	{
		std::ostringstream ss;
		
		if( context.locals().find(id.name) != context.locals().end()) 
		{
			//variable exist - switch it's value
			std::cerr<<"Container variable redefinition - currently not supported" <<std::endl;

			#ifdef DEBUG
				std::cout<<"Container variable definition exists - switching" <<std::endl;
			#endif
		} else 
		{
			
			if (assignExpression->getClassType() == LIST){
				std::string* underlyingType = assignExpression->getType();
				ss << *id.generateCode(context) << "[" << *assignExpression->getNumberOfElements() << "]";
				ss << *assignExpression->generateCode(context);
				ss << ";\n";
				ss << *underlyingType << "* dev_" << *id.generateCode(context) << ";" << std::endl;
				ss << "HANDLE_ERROR( cudaMalloc( (void**)&dev_" << *id.generateCode(context)<<  ", sizeof("<< *underlyingType << ")*" << *assignExpression->getNumberOfElements() << " ) );" << std::endl;
				ss << "HANDLE_ERROR( cudaMemcpy( dev_" << *id.generateCode(context) << ", "<< *id.generateCode(context) << ", " << *assignExpression->getNumberOfElements() << " * sizeof(" << *underlyingType <<") , cudaMemcpyHostToDevice) )" << std::endl;
			}
			else if (assignExpression->getClassType() == LISTBINARYOP){
				std::ostringstream v0;
				v0 << MTNode::tmpVariable << MTNode::tmpVariableCount;
				std::string var0 = v0.str(); 
				std::string* underlyingType = assignExpression->getType();

				ss << *underlyingType << " " << var0 << "[" << *assignExpression->getNumberOfElements() << "]" << " = " << "{ 0 }" << ";" << "\n";
				ss << *underlyingType << " ";	
				ss << "*" << *id.generateCode(context);
				ss << *assignExpression->generateCode(context);
			}
			else if (assignExpression->getClassType() == MATRIX){
				std::string* underlyingType = assignExpression->getType();
				ss << *id.generateCode(context) << "[" << *assignExpression->getNumberOfElements() << "]";
				ss << *assignExpression->generateCode(context);
				ss << ";\n";
				ss << *underlyingType << "* dev_" << *id.generateCode(context) << ";" << std::endl;
				ss << "HANDLE_ERROR( cudaMalloc( (void**)&dev_" << *id.generateCode(context)<<  ", sizeof("<< *underlyingType << ")*" << *assignExpression->getNumberOfElements() << " ) );" << std::endl;
				ss << "HANDLE_ERROR( cudaMemcpy( dev_" << *id.generateCode(context) << ", "<< *id.generateCode(context) << ", " << *assignExpression->getNumberOfElements() << " * sizeof(" << *underlyingType <<") , cudaMemcpyHostToDevice) )" << std::endl;
			
			}
			else if (assignExpression->getClassType() == MATRIXBINARYOP){
				std::ostringstream v0;
				v0 << MTNode::tmpVariable << MTNode::tmpVariableCount;
				std::string var0 = v0.str(); 
				std::string* underlyingType = assignExpression->getType();

				ss << *underlyingType << " " << var0 << "[" << *assignExpression->getNumberOfElements() << "]" << " = " << "{ 0 }" << ";" << "\n";
				ss << *underlyingType << " ";	
				ss << "*" << *id.generateCode(context);
				ss << *assignExpression->generateCode(context);
			}
			
			//CUDA part of generated code

			//ss << *underlyingType << "* dev_" << *id.generateCode(context) << ";" << std::endl;
			//ss << "HANDLE_ERROR( cudaMalloc( (void**)&dev_" << *id.generateCode(context)<<  ", sizeof("<< *underlyingType << ")*" << *assignExpression->getNumberOfElements() << " ) );" << std::endl;
			//ss << "HANDLE_ERROR( cudaMemcpy( dev_" << *id.generateCode(context) << ", "<< *id.generateCode(context) << ", " << *assignExpression->getNumberOfElements() << " * sizeof(" << *underlyingType <<") , cudaMemcpyHostToDevice) );" << std::endl;
			
			#ifdef DEBUG
				std::cout<<"Container variable definition created" <<std::endl;
			#endif
		}
		std::string* generatedValue = new std::string(ss.str()); 

		//place new/modified value back to locals()
		context.locals()[id.name] = generatedValue;

		return generatedValue;
	}
}